#ifndef _ASYNCH_ACCEPTOR_CPP_
#define _ASYNCH_ACCEPTOR_CPP_

#include "AsynchAcceptor.h"

template<typename HANDLER>
AsynchAcceptor<HANDLER>::AsynchAcceptor ()
: listen_handle_ (NDK_INVALID_HANDLE)
, num_of_accepts_one_time_ (-1)
{

}
template<typename HANDLER>
AsynchAcceptor<HANDLER>::~AsynchAcceptor ()
{
    if (this->listen_handle_ != NDK_INVALID_HANDLE)
    {
        NDK_OS::closesocket (this->listen_handle_);
        this->listen_handle_ = NDK_INVALID_HANDLE;
    }
}
template<typename HANDLER>
int AsynchAcceptor<HANDLER>::open (const InetAddr &address,
        Proactor *proactor/* = Proactor::instance ()*/,
        int reuse_addr/* = 1*/,
        size_t rcvbuf_size/* = 0*/,
        int protocol_family/* = AF_INET*/,
        int backlog/* = NDK_DEFAULT_BACKLOG*/,
        int num_of_accepts_one_time/* = -1*/)
{
    TRACE ("");
    if (proactor == 0 || num_of_accepts_one_time == 0)
        return -1;
    this->proactor (proactor);
    if (num_of_accepts_one_time == -1)
        num_of_accepts_one_time = backlog;
    this->num_of_accepts_one_time_ = num_of_accepts_one_time;

    // Create the listener socket
    this->listen_handle_ = NDK_OS::socket (address.get_type (), 
            SOCK_STREAM, 
            0);
    if (this->listen_handle_ == NDK_INVALID_HANDLE)
        return -1;

    int result = 0;
    // Initialize the AsynchAccept
    if (this->asynch_accept_.open (this,
                this->listen_handle_,
                0,
                this->proactor ()) != 0)
        result = -1;
    if (result != -1 && reuse_addr)
    {
        // Reuse the address
        int on = 1;
        if (NDK_OS::setsock_reuseaddr (this->listen_handle_, on) != 0)
            result = -1;
    }
    if (result != -1 && rcvbuf_size != 0)
    {
        result = NDK_OS::setsock_rcvbuf (this->listen_handle_, rcvbuf_size);
    }
    // If port is not specified, bind to any port.
    if (result != -1)
    {
        if (address.get_port_number () == 0)
        {
            sockaddr_in local_inet_addr;
            ::memset (reinterpret_cast<void *> (&local_inet_addr),
                    0,
                    sizeof (local_inet_addr));
            local_inet_addr = *reinterpret_cast<sockaddr_in *>(
                    address.get_addr ());
            if (NDK::bind_port (this->listen_handle_,
                        ntohl(local_inet_addr.sin_addr.s_addr), 
                        protocol_family) != 0)
                result = -1;
        }else
        {
            if (NDK_OS::bind (this->listen_handle_,
                        reinterpret_cast<sockaddr *> (address.get_addr ()),
                        address.get_addr_size ()) != 0)
                result = -1;
        }
    }
    // Start listening.
    if (result != -1 && NDK_OS::listen (this->listen_handle_, backlog) != 0)
        result = -1;
    if (result != -1)
    {
        int space = sizeof (ConnectionInfo) * this->num_of_accepts_one_time_;
        MessageBlock *mb = new MessageBlock(space);
        if (this->asynch_accept_.accept (mb,
                    this->num_of_accepts_one_time_) != 0)
            result = -1;
    }
    if (result == -1)
    {
        NDK_OS::closesocket (this->listen_handle_);
        this->listen_handle_ = NDK_INVALID_HANDLE;
        return -1;
    }
    return result;
}
template<typename HANDLER>
int AsynchAcceptor<HANDLER>::handle_accept (const AsynchAccept::Result &result)
{
    TRACE ("");
    int error = 0;
    // If the asynchronous accept fails.
    MessageBlock *message_block = result.message_block ();
    if (!result.success () && 
            message_block->length () >= sizeof (ConnectionInfo))
        error = 1;

    if (!error)
    {
        for (   ;
                message_block->length () >= sizeof (ConnectionInfo);
                message_block->rd_ptr (sizeof (ConnectionInfo)))
        {
            ConnectionInfo conn_info;
            ::memcpy (&conn_info, 
                    message_block->rd_ptr (), 
                    sizeof (ConnectionInfo));
#if defined NDK_WIN32
            /**
             * In order to use accept handle with other Window Sockets 1.1
             * functions, we call the setsockopt function with the
             * SO_UPDATE_ACCEPT_CONTEXT option. This option initializes the
             * socket so that other Windows Sockets routines to access the
             * socket correctly.
             */
            if (::setsockopt (conn_info.accept_handle,
                        SOL_SOCKET,
                        SO_UPDATE_ACCEPT_CONTEXT,
                        (char *) &this->listen_handle_,
                        sizeof (this->listen_handle_)) == -1)
            {
                if (conn_info.accept_handle != NDK_INVALID_HANDLE)
                    NDK::closesocket (conn_info.accept_handle);
                continue;
            }
#endif // NDK_WIN32

            HANDLER *new_handler = 0;
            if (!error)
            {
                // The Template method
                new_handler = this->make_handler ();
                if (new_handler == 0)
                {
                    if (conn_info.accept_handle != NDK_INVALID_HANDLE)
                        NDK_OS::closesocket (conn_info.accept_handle);
                    continue;
                }
                new_handler->proactor (this->proactor ());

                // Set up the handler's new handle value
                new_handler->handle (conn_info.accept_handle);

                new_handler->remote_addr(conn_info.remote_address);

                // Initiate the handler
                if (new_handler->open (conn_info.accept_handle) != 0)
                {
                    new_handler->handle_close (AsynchHandler::READ_MASK);
                }
            }
        }
    }
    message_block->reset ();
    this->asynch_accept_.accept (message_block,
            this->num_of_accepts_one_time_);
    return 0;
}
template <class HANDLER> 
int AsynchAcceptor<HANDLER>::handle_close (const int )
{
    NDK_OS::closesocket (this->listen_handle_);
    return 0;
}
template <class HANDLER> 
HANDLER *AsynchAcceptor<HANDLER>::make_handler (void)
{
    TRACE ("");
    return new HANDLER();
}
template <class HANDLER>
NDK_HANDLE AsynchAcceptor<HANDLER>::handle (void) const
{
    TRACE ("");
    return this->listen_handle_;
}
#endif

